/*
 * Copyright (c) 2009, Takashi Ito
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the authors nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "tssp_internal.h"
#include "tssp_dvb_dr_02.h"


int tssp_dr_transport_protocol_parse(struct tssp_dr_transport_protocol *dest,
                                     const struct tssp_descriptor *src)
{
    int i;
    int j;
    int n;
    int pos;
    struct tssp_dr_OC_transport *oc;
    struct tssp_dr_IC_transport *ic;
    uint8_t base_length;
    uint8_t ext_length;
    uint8_t *data = src->data;

    TSSP_ASSERT(dest);
    TSSP_ASSERT(src);

    if (src->tag != 0x02)
        return TSSP_STATUS_FAILED;

    if (src->length < 3)
        return TSSP_STATUS_FAILED;

    dest->protocol_id = (data[0] << 8) + data[1];
    dest->label = data[2];

    if (dest->protocol_id == 0x0001) {

        if (src->length < 5)
            return TSSP_STATUS_FAILED;

        data += 3;
        oc = &dest->selector_byte.oc;
        oc->remote_connection = 0x80 & data[0];
        if (oc->remote_connection) {
            oc->original_network_id = (data[1] << 8) + data[2];
            oc->transport_stream_id = (data[3] << 8) + data[4];
            oc->service_id = (data[5] << 8) + data[6];
            data += 6;
        }
        oc->component_tag = data[1];

    } else if (dest->protocol_id == 0x0003) {

        pos = 3;

        /* initialize */
        dest->selector_byte.ic.count = 0;

        if (src->length == pos)
            return TSSP_STATUS_FAILED;

        memcpy(dest->selector_byte.ic.data, &data[pos + 1], src->length - pos - 1);
        base_length = data[pos];
        data = &dest->selector_byte.ic.data[-(pos + 1)];

        /* outer loop */
        i = n = 0;
        while ((pos + 2) <= src->length && src->length >= (pos + 2 + base_length)) {
            dest->selector_byte.ic.count++;
            ic = &dest->selector_byte.ic.ic[i];

            /* URL_base_byte */
            ic->URL_base = &data[pos + 1];
            pos += 1 + base_length;
            /* URL_extension_count */
            ic->URL_extension_count = data[pos];
            /* write zero termination */
            ic->URL_base[base_length] = '\0';
            pos++;

            /* URL_extension */
            ic->URL_extension = &dest->selector_byte.ic.list[n];

            /* check the length */
            if (src->length <= pos) {
                if (ic->URL_extension_count != 0)
                    return TSSP_STATUS_FAILED;
            }

            /* inner loop */
            ext_length = data[pos];
            for (j = 0; j < ic->URL_extension_count; j++) {
                /* check the length */
                if (src->length < (pos + 1 + ext_length))
                    return TSSP_STATUS_FAILED;
                /* set URL_extension */
                ic->URL_extension[j] = &data[pos + 1];
                /* store the next extension length */
                pos += 1 + ext_length;
                ext_length = data[pos];
                /* write zero termination for URL_extension */
                data[pos] = '\0';
            }
            n += ic->URL_extension_count;

            /* store the next URL_base_length */
            base_length = ext_length;
            i++;
        }
        if (src->length != pos)
            return TSSP_STATUS_FAILED;

    } else {

        dest->selector_byte.chunk.length = src->length - 3;
        memcpy(dest->selector_byte.chunk.data, &src->data[3], src->length - 3);

    }

    return TSSP_STATUS_OK;
}

struct tssp_dr_transport_protocol *
tssp_dr_transport_protocol_create(const struct tssp_descriptor *src)
{
    struct tssp_dr_transport_protocol *dest;

    dest = malloc(sizeof(*dest));
    if (dest == NULL)
        return NULL;

    if (tssp_dr_transport_protocol_parse(dest, src) == TSSP_STATUS_OK)
        return dest;

    free(dest);
    return NULL;
}

